//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  Low level interfaces that allow separate components to use objects that are defined
//               in completely independant libraries so long as they satisfy the basic interface requirements.
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either express or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is MapWindow.Interfaces
//
// The Initial Developer of this Original Code is Ted Dunsford. Created in August, 2007.
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using MapWindow.Main;
using MapWindow.Geometries;

namespace MapWindow.Drawing
{
    /// <summary>
    /// This is a collection of layers to draw, bound by a specific view.  This allows
    /// the legend, map, and layout to easilly share the same items collection.
    /// </summary>
    public class DataFrame : IDataFrame
    {
        /// <summary>
        /// Contains the base list of layers.  Since specific layer types inherit from
        /// IMapLayer, these can generally be cast into the necessary types.
        /// </summary>
        protected EventDictionary<int, IMapLayer> _MapLayers;

        /// <summary>
        /// Contains the base envelope
        /// </summary>
        protected IEnvelope _Envelope;

        /// <summary>
        /// If this is true, (the default), then the draw method will use the BackBufferEnvelope.
        /// Otherwise, the Envelope will be used.
        /// </summary>
        protected bool _DrawToBackBuffer;


        #region Constructors

        DataFrame()
        {
            _DrawToBackBuffer = true; // Defaults to drawing to the back buffer
        }

        #endregion

        /// <summary>
        /// If this is true, (the default), then the draw method will use the BackBufferEnvelope.
        /// Otherwise, the Envelope will be used.
        /// </summary>
        public bool DrawToBackBuffer
        {
            get { return _DrawToBackBuffer; }
            set { _DrawToBackBuffer = value; }
        }

        /// <summary>
        /// Gets or sets the view extents in world coordinates.  Since drawing should be done
        /// to the back buffer, this extents is more useful for scaling calculations.  This, however
        /// is where the backbuffer envelope is controlled since it is calculated as needed from
        /// this envelope.
        /// </summary>
        public IEnvelope Envelope
        {
            get
            {
                return _Envelope;

            }
            set
            {
                _Envelope = value;
            }
        }

        /// <summary>
        /// When panning, the user has to drag one point location on the map to another.  If the
        /// backbuffer is large enough, you can pan and always have an image on the backbuffer
        /// of the appropriate information.  This, then, describes the contents of the back
        /// buffer for drawing purposes.
        /// </summary>
        public IEnvelope BackBufferEnvelope
        {
            get
            {
                Envelope env = new Envelope(_Envelope);
                env.ExpandBy(Math.Max(_Envelope.Height, _Envelope.Width));
                return env;
            }
        }

        /// <summary>
        /// Gets all of the map layers, regardless of their type as a 
        /// MapWindow.Main.IEventDictionary of MapWindow.Drawing.MapLayers.
        /// These can be further cast into their specific types.
        /// </summary>
        public IEventDictionary<int, IMapLayer> MapLayers
        {
            get
            {
                return _MapLayers;
            }
        }

        /// <summary>
        /// This will instruct each of the layers to draw themselves on the specified graphics surface
        /// with the specified clipRect (where clipRect is in world coordinates.)  If the clipRect
        /// is Empty (the usual case) then this will substitute the world coordinate Envelope
        /// or BackBufferEnvelope depending on which option is selected.
        /// </summary>
        /// <param name="g">The drawing graphics surface to draw on.</param>
        /// <param name="clipRect">This specified the clip rectangle in world coordinates, since
        /// it is assumed that the Transform has already been performed on g to account for the
        /// scale and translation.  If this is empty, however, then the appropriate dataframe
        /// clipRect will be used.</param>
        public void Draw(System.Drawing.Graphics g, RectangleF clipRect)
        {
           
            // Set an appropriate clipRect if one hasn't been assigned
            if (clipRect == RectangleF.Empty)
            {
                if (_DrawToBackBuffer == true)
                {
                    clipRect = BackBufferEnvelope.RectangleF;
                }
                else
                {
                    clipRect = Envelope.RectangleF;
                }
            }

            // Allow the possibility for canceling eventhandlers.  
            // clipRect should now exist.
            if(OnBeforeDrawing(g, clipRect) == true) return; // Canceled if true 

            if (clipRect != RectangleF.Empty)
            {
                IEnumerator<KeyValuePair<int, IMapLayer>> layer = _MapLayers.GetEnumerator();
                while (layer.MoveNext())
                {
                    layer.Current.Value.Draw(g, clipRect);
                    
                }
            }

            // Allow event handlers that continue special drawing AFTER the drawing event
            OnAfterDrawing(g, clipRect);
            
        }

        public event EventHandler<MapWindow.Main.EventArgs.CancelPaint> BeforeDrawing;

        protected bool OnBeforeDrawing(Graphics g, RectangleF clipRect)
        {
            if(BeforeDrawing == null)return false;
            MapWindow.Main.EventArgs.CancelPaint e = new MapWindow.Main.EventArgs.CancelPaint(g, clipRect);
            BeforeDrawing(this, e);
            return e.Cancel;
        }

        public event EventHandler<MapWindow.Main.EventArgs.Paint> AfterDrawing;

        protected void OnAfterDrawing(Graphics g, RectangleF clipRect)
        {
            if (AfterDrawing == null) return;
            MapWindow.Main.EventArgs.Paint e = new MapWindow.Main.EventArgs.Paint(g, clipRect);
            AfterDrawing(this, e);
        }
    

      
    }
}
